﻿// No word splitting is allowed, thus all the following methods are for research IDEAS only
///////////////////////////////////////////////////////////////////////////////////////////
// ٦_١١٥	وتمت كلمت ربك صدقا وعدلا لا مبدل لكلمته وهو السميع العليم
// 6:115	And the Words of your Lord has been prefected full of Truth and Justice:
//          None can change His Words, and He is the All-hearing, All-knowing.
///////////////////////////////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.Text;

using DigitalQuran.Collection;
using DigitalQuran.Extensions;
using DigitalQuran.Numerology;
using DigitalQuran.IO;

namespace DigitalQuran.Research
{
    /// <summary>
    /// Statistical Research Methods
    /// </summary>
    public static class StaticalResearch
    {           
        [ResearchMethod(Edition.Research)]
        public static void KeyLettersOnly(ResearchAgent agent)
        {
            var df = new DataFrame("#", "Text");
            foreach (Verse verse in agent.Client.Selection.Verses)
            {
                df.AddRow(verse.GlobalNumber,
                    verse.ToString(agent.Client.ActiveTextMode)
                    .Replace("ف", "").Replace("ف", "").Replace("ز", "")
                    .Replace("ء", "").Replace("خ", "").Replace("ش", "")
                    .Replace("ظ", "").Replace("ج", "").Replace("ث", ""));
            }

            agent.SaveCsv(agent.Client.Numerology.ActiveSystem.ToString() + "_KeyLettersOnly", df);
        }

        [ResearchMethod(Edition.Research)]
        public static void InitialLettersOnly(ResearchAgent agent)
        {            
            var str = new StringBuilder(agent.Client.Selection.ToString(agent.Client.ActiveTextMode));
            
            str.Replace("ب", "").Replace("د", "").Replace("و", "").Replace("ت", "")
                .Replace("ذ", "").Replace("غ", "").Replace("ض", "").Replace("ف", "")
                .Replace("ز", "").Replace("ء", "").Replace("خ", "").Replace("ش", "")
                .Replace("ظ", "").Replace("ج", "").Replace("ث", "");

            agent.SaveText(agent.Client.Numerology.ActiveSystem.ToString() + "_InitialLettersOnly", str.ToString());            
        }

        [ResearchMethod()]
        public static void PrimeLetters(ResearchAgent agent)
        {
            if (agent.Client.Selection == null) return;

            var versesText = agent.Client.Selection.ToString(agent.Client.ActiveTextMode).Replace("\r", "").Replace("\n", "");
            int count = versesText.Length;
            var df = new DataFrame("#", "Letter");

            int l = 1; // letter index
            for (int i = 0; i < count; i++)
            {
                if (l.IsPrime()) {
                    df.AddRow(i, versesText[i]);
                }
                l++;
            }
            
            agent.SaveCsv(agent.Client.Numerology.ActiveSystem.ToString() + "_PrimeLetters", df);            
        }

        [ResearchMethod()]
        public static void AdditivePrimeLetters(ResearchAgent agent)
        {
            if (agent.Client.Selection == null) return;

            var versesText = agent.Client.Selection.ToString(agent.Client.ActiveTextMode).Replace("\r", "").Replace("\n", "");
            int count = versesText.Length;

            var df = new DataFrame("#", "Letter");

            int l = 1; // letter index
            for (int i = 0; i < count; i++)
            {
                if (l.IsAdditivePrime()) {
                    df.AddRow(l, versesText[i]);                    
                }
                l++;
            }
            
            agent.SaveCsv(agent.Client.Numerology.ActiveSystem.ToString() + "_AdditivePrimeLetters", df);   
        }

        [ResearchMethod()]
        public static void PurePrimeLetters(ResearchAgent agent)
        {
            if (agent.Client.Selection == null) return;

            var versesText = agent.Client.Selection.ToString(agent.Client.ActiveTextMode).Replace("\r", "").Replace("\n", "");

            var df = new DataFrame("#", "Letter");

            int count = versesText.Length;

            int l = 1; // letter index
            for (int i = 0; i < count; i++)
            {
                if (l.IsPurePrime()) {
                    df.AddRow(l, versesText[i]);                    
                }
                l++;
            }
            
            agent.SaveCsv(agent.Client.Numerology.ActiveSystem.ToString() + "_PurePrimeLetters", df);            
        }

        [ResearchMethod()]
        public static void FibonacciLetters(ResearchAgent agent)
        {
            // 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...
            if (agent.Client.Selection == null) return;

            var versesText = agent.Client.Selection.ToString(agent.Client.ActiveTextMode).Replace("\r", "").Replace("\n", "_");
            int count = versesText.Length;
            var df = new DataFrame("#", "Letter");

            int l = 1; // letter index
            for (int i = 0; i < count; i++)
            {
                if (Numbers.Fibonaccis.Contains(l)) {
                    df.AddRow(l, versesText[i]);
                }
                l++;
            }            

            string path = agent.Client.Numerology.ActiveSystem.ToString() + "_FibonacciLetters";
            agent.SaveCsv(path, df);            
        }

        [ResearchMethod()]
        public static void PrimeWords(ResearchAgent agent)
        {
            var textMode = agent.Client.ActiveTextMode;
            var df = new DataFrame("#", "Address", "Text");
            foreach (var verse in agent.Client.Selection.Verses) {
                foreach (var word in verse.Words) {
                    if (word.GlobalNumber.IsPrime())
                        df.AddRow(word.GlobalNumber, word.Address, word.ToString(textMode));
                }
            }
            
            agent.SaveCsv(agent.Client.Numerology.ActiveSystem.ToString() + "_PrimeWords", df);
        }

        [ResearchMethod()]
        public static void AdditivePrimeWords(ResearchAgent agent)
        {
            var textMode = agent.Client.ActiveTextMode;
            var df = new DataFrame("#", "Address", "Text");
            foreach (var verse in agent.Client.Selection.Verses) {
                foreach (var word in verse.Words) {
                    if (word.GlobalNumber.IsAdditivePrime())
                        df.AddRow(word.GlobalNumber, word.Address, word.ToString(textMode));
                }
            }

            agent.SaveCsv(agent.Client.Numerology.ActiveSystem.ToString() + "_AdditivePrimeWords", df);            
        }

        [ResearchMethod()]
        public static void PurePrimeWords(ResearchAgent agent)
        {
            var textMode = agent.Client.ActiveTextMode;
            var df = new DataFrame("#", "Address", "Text");
            foreach (var verse in agent.Client.Selection.Verses) {
                foreach (var word in verse.Words) {
                    if (word.GlobalNumber.IsPurePrime())
                        df.AddRow(word.GlobalNumber, word.Address, word.ToString(textMode));
                }
            }

            agent.SaveCsv(agent.Client.Numerology.ActiveSystem.ToString() + "_PurePrimeWords", df);            
        }

        [ResearchMethod()]
        public static void FibonacciWords(ResearchAgent agent)
        {
            var textMode = agent.Client.ActiveTextMode;
            var df = new DataFrame("#", "Address", "Text");
            foreach (var verse in agent.Client.Selection.Verses) {
                foreach (var word in verse.Words) {
                    if (Numbers.Fibonaccis.Contains(word.GlobalNumber))
                        df.AddRow(word.GlobalNumber, word.Address, word.ToString(textMode));
                }
            }

            agent.SaveCsv(agent.Client.Numerology.ActiveSystem.ToString() + "_FibonacciWords", df);            
        }

        [ResearchMethod]
        public static void AllahWords(ResearchAgent agent)
        {
            var df = new DataFrame("#", "Address", "Text", "Verse", "Word");
            try {
                int count = 0;
                string text;

                foreach (Verse verse in agent.Client.Selection.Verses) {
                    foreach (Word word in verse.Words) {
                        text = word.ToString(agent.Client.ActiveTextMode);

                        if (text == "الله" || text == "ءالله" || text == "ابالله" ||
                            text == "اللهم" || text == "بالله" || text == "تالله" ||
                            text == "فالله" || text == "والله" || text == "وتالله" ||
                            text == "لله" || text == "فلله" || text == "ولله") {
                            count++;
                            df.AddRow(count, word.Address.ToString(), text, verse.Address.ToString(), word.NumberInVerse);
                        }
                    }
                }
            } catch (Exception ex) {
                df.Exceptions.Add(ex);
            }

            agent.SaveCsv("AllahWords", df);
        }

        [ResearchMethod]
        public static void NotAllahWords(ResearchAgent agent)
        {
            var df = new DataFrame("#", "Address", "Text", "Verse", "Word");
            try {
                int count = 0;
                string text;
                foreach (Verse verse in agent.Client.Selection.Verses) {
                    foreach (Word word in verse.Words) {
                        text = word.ToString(agent.Client.ActiveTextMode);

                        if (text == "الضلله" || text == "الكلله" || text == "خلله" || text == "خللها" ||
                            text == "خللهما" || text == "سلله" || text == "ضلله" || text == "ظلله" ||
                            text == "ظللها" || text == "كلله" || text == "للهدي" || text == "وظللهم" ||
                            text == "يضلله" || text == "اللهب" || text == "اللهو") {
                            count++;
                            df.AddRow(count, word.Address.ToString(), text, verse.Address.ToString(), word.NumberInVerse);
                        }
                    }
                }
            } catch (Exception ex) {
                df.Exceptions.Add(ex);
            }
            
            agent.SaveCsv("NotAllahWords", df);
        }

        [ResearchMethod]
        public static void WordMeaning(ResearchAgent agent) {
            var df = new DataFrame("Address", "Chapter", "Verse", "Word", "Text", "Meaning");

            foreach (Verse verse in agent.Client.Selection.Verses) {
                foreach (Word word in verse.Words) {
                    try {
                        df.AddRow(
                            word.Address,
                            verse.Chapter.GlobalNumber.ToString(),
                            verse.NumberInChapter.ToString(),
                            word.NumberInVerse.ToString(),
                            word.ToString(agent.Client.ActiveTextMode),
                            word.Meaning);
                    } catch (Exception ex) {
                        df.Exceptions.Add(ex);
                    }
                }
            }

            agent.SaveCsv("WordMeaning", df);
        }

        [ResearchMethod]
        public static void WordTransliteration(ResearchAgent agent) {
            var df = new DataFrame("Address", "Chapter", "Verse", "Word", "Text", "Transliteration");

            foreach (Verse verse in agent.Client.Selection.Verses) {
                foreach (Word word in verse.Words) {
                    try {
                        df.AddRow(
                            word.Address,
                            verse.Chapter.GlobalNumber,
                            verse.NumberInChapter,
                            word.NumberInVerse,
                            word.ToString(agent.Client.ActiveTextMode),
                            word.Transliteration);
                    } catch (Exception ex) {
                        df.Exceptions.Add(ex);
                    }
                }
            }

            agent.SaveCsv("WordTransliteration", df);
        }

        [ResearchMethod]
        public static void WordRoots(ResearchAgent agent) {
            var df = new DataFrame("Address", "Chapter", "Verse", "Word", "Text", "Roots");

            foreach (Verse verse in agent.Client.Selection.Verses) {
                foreach (Word word in verse.Words) {
                    try {
                        df.AddRow(
                            word.Address,
                            verse.Chapter.GlobalNumber,
                            verse.NumberInChapter,
                            word.NumberInVerse,
                            word.ToString(agent.Client.ActiveTextMode),
                            word.Roots.ToString("|"));
                    } catch (Exception ex) {
                        df.Exceptions.Add(ex);
                    }
                }
            }

            agent.SaveCsv("WordRoots", df);
        }

        class WordInfo {
            internal WordInfo(Address adress) {
                Addresses.Add(adress);
            }

            internal int Frequency = 1;
            internal List<Address> Addresses = new List<Address>();
        }

        [ResearchMethod]
        public static void WordFrequency(ResearchAgent agent) {
            var df = new DataFrame("Word", "Frequency", "Addresses");

            var wordInfo = new Dictionary<string, WordInfo>();
            foreach (Verse verse in agent.Client.Selection.Verses) {
                foreach (Word word in verse.Words) {
                    try {
                        string text = word.ToString(agent.Client.ActiveTextMode);
                        if (!wordInfo.ContainsKey(text)) {
                            wordInfo.Add(text, new WordInfo(word.Address));
                        } else // word already exists
                        {
                            wordInfo[text].Frequency++;
                            wordInfo[text].Addresses.Add(word.Address);
                        }
                    } catch (Exception ex) {
                        df.Exceptions.Add(ex);
                    }
                }
            }

            foreach (string key in wordInfo.Keys) {
                df.AddRow(key, wordInfo[key].Frequency, wordInfo[key].Addresses.ToString("|"));
            }

            agent.SaveCsv("WordFrequency", df);
        }

        [ResearchMethod]
        public static void WordOccurrences(ResearchAgent agent) {
            var df = new DataFrame("Address", "Text", "Occurrence", "Frequency");

            Dictionary<Address, int> wordOccurrences;
            Dictionary<Address, int> wordFrequencies;
            DoCalculateWordOccurencesAndFrequencies(agent, agent.Client.Selection.Verses, out wordOccurrences, out wordFrequencies);

            foreach (Verse verse in agent.Client.Selection.Verses) {
                foreach (Word word in verse.Words) {
                    df.AddRow(
                        word.Address,
                        word.ToString(agent.Client.ActiveTextMode),
                        wordOccurrences[word.Address],
                        wordFrequencies[word.Address]);
                }
            }

            agent.SaveCsv("WordOccurrences", df);
        }

        [ResearchMethod]
        public static void WordInformation(ResearchAgent agent) {
            DataFrame df;
            Dictionary<Address, int> wordOccurrences;
            Dictionary<Address, int> wordFrequencies;
            DoCalculateWordOccurencesAndFrequencies(agent, agent.Client.Selection.Verses, out wordOccurrences, out wordFrequencies);

            if (Globals.Edition == Edition.Grammar) {
                df = new DataFrame("Address", "Chapter", "Verse", "Word", "Text", "Transliteration",
                    "Roots", "Root", "Lemma", "SpecialGroup", "Meaning", "Occurrence", "Frequency");

                foreach (Verse verse in agent.Client.Selection.Verses) {
                    foreach (Word word in verse.Words) {
                        df.AddRow(
                            word.Address,
                            verse.Chapter.GlobalNumber,
                            verse.NumberInChapter,
                            word.NumberInVerse,
                            word.ToString(agent.Client.ActiveTextMode),
                            word.Transliteration,
                            word.Roots.ToString("|"),
                            word.CorpusRoot,
                            word.CorpusLemma,
                            word.CorpusSpecialGroup,
                            word.Meaning,
                            wordOccurrences[word.Address],
                            wordFrequencies[word.Address]);
                    }
                }

            } else {
                df = new DataFrame("Address", "Chapter", "Verse", "Word", "Text", "Transliteration", "Roots",
                    "Meaning", "Occurrence", "Frequency");

                foreach (Verse verse in agent.Client.Selection.Verses) {
                    foreach (Word word in verse.Words) {
                        df.AddRow(
                            word.Address,
                            verse.Chapter.GlobalNumber,
                            verse.NumberInChapter,
                            word.NumberInVerse,
                            word.ToString(agent.Client.ActiveTextMode),
                            word.Transliteration,
                            word.Roots.ToString("|"),
                            word.Meaning,
                            wordOccurrences[word.Address],
                            wordFrequencies[word.Address]);
                    }
                }
            }

            agent.SaveCsv("WordInformation", df);
        }

        [ResearchMethod]
        public static void WordPartInformation(ResearchAgent agent) {
            var df = new DataFrame("Address", "Chapter", "Verse", "Word", "Part", "Text",
                "Buckwalter", "Tag", "Type", "Position", "Attribute", "Qualifier",
                "PersonDegree", "PersonGender", "PersonNumber", "Mood", "Lemma",
                "Root", "SpecialGroup", "WordAddress");

            foreach (Verse verse in agent.Client.Selection.Verses) {
                foreach (Word word in verse.Words) {
                    foreach (WordPart part in word.Parts) {
                        df.AddRow(
                            part.Address,
                            part.Word.Verse.Chapter.GlobalNumber,
                            part.Word.Verse.NumberInChapter,
                            part.Word.NumberInVerse,
                            part.NumberInWord,
                            part.Text,
                            part.Buckwalter,
                            part.Tag,
                            part.Features.ToString(),
                            part.Word.Address
                        );
                    }
                }
            }

            agent.SaveCsv("WordPartInformation", df);
        }

        private static void DoCalculateWordOccurencesAndFrequencies(ResearchAgent agent, VerseCollection verses, out Dictionary<Address, int> wordOccurrences, out Dictionary<Address, int> wordFrequencies) {
            wordFrequencies = new Dictionary<Address, int>();
            wordOccurrences = new Dictionary<Address, int>();
            Dictionary<string, int> textFrequencies = new Dictionary<string, int>();

            string text;
            foreach (Verse verse in verses) {
                foreach (Word word in verse.Words) {
                    text = word.ToString(agent.Client.ActiveTextMode);
                    if (textFrequencies.ContainsKey(text)) {
                        textFrequencies[text]++;
                        wordOccurrences[word.Address] = textFrequencies[text];
                    } else {
                        textFrequencies.Add(text, 1);
                        wordOccurrences.Add(word.Address, 1);
                    }
                }
            }

            // sum up all word_text frequencies for all word_addresses with the same word_text
            foreach (Verse verse in verses) {
                foreach (Word word in verse.Words) {
                    foreach (string key in textFrequencies.Keys) {
                        text = word.ToString(agent.Client.ActiveTextMode);
                        if (key == text) {
                            if (wordFrequencies.ContainsKey(word.Address)) {
                                wordFrequencies[word.Address] += textFrequencies[text];
                            } else {
                                wordFrequencies.Add(word.Address, textFrequencies[text]);
                            }
                        }
                    }
                }
            }
        }

        [ResearchMethod]
        public static void VerseStatistics(ResearchAgent agent) {

            var nSystem = agent.Client.Numerology.ActiveSystem;

            var df = new DataFrame("#", "Page", "Chapter", "Verse", "Words", "Letters",
                "Unique", "Value", "Prime", "Additive Prime", "Pure Prime", "Composite",
                "Additive Composite", "Pure Composite");

            foreach (var key in nSystem.Keys)
                df.Columns.Add(key.ToString(), typeof(object));

            df.Columns.Add("PrimeFactors", typeof(object));
            df.Columns.Add("Text", typeof(object));

            foreach (Verse verse in agent.Client.Selection.Verses) {
                int prime = Array.BinarySearch<long>(Numbers.Primes, verse.Value);
                int additPrime = Array.BinarySearch<long>(Numbers.AdditivePrimes, verse.Value);
                int purePrime = Array.BinarySearch<long>(Numbers.PurePrimes, verse.Value);
                int composite = Array.BinarySearch<long>(Numbers.Composites, verse.Value);
                int additComposit = Array.BinarySearch<long>(Numbers.AdditiveComposites, verse.Value);
                int pureComposit = Array.BinarySearch<long>(Numbers.PureComposites, verse.Value);

                df.AddEnumeratorRow(new DataFrame.ItemModifierDelegate((v) => { return verse.Words.GetLetterFrequency((char)v); }),
                    verse.GlobalNumber,
                    verse.Page.GlobalNumber,
                    verse.Chapter.GlobalNumber,
                    verse.NumberInChapter,
                    verse.Words.Count,
                    verse.Words.LetterCount,
                    verse.UniqueLetters.Count,
                    verse.Value,
                    prime == -1 ? (object)"-" : prime,
                    additPrime == -1 ? (object)"-" : additPrime,
                    purePrime == -1 ? (object)"-" : purePrime,
                    composite == -1 ? (object)"-" : composite,
                    additComposit == -1 ? (object)"-" : additComposit,
                    pureComposit == -1 ? (object)"-" : pureComposit,
                    nSystem.Keys.GetEnumerator(),
                    verse.Value.Factorize().ToString(", "),
                    verse.ActiveText
                    );
            }

            agent.SaveCsv(agent.Client.Numerology.ActiveSystem.ToString() + "_VerseStatistics", df);
        }

        [ResearchMethod]
        public static void ChapterStatistics(ResearchAgent agent) {
            var nSystem = agent.Client.Numerology.ActiveSystem;

            var df = new DataFrame("#", "Name", "Page", "Verses", "Words", "Letters", "Unique", "Value",
                "Primes", "Additive Primes", "Pure Primes", "Composites", "Additive Composites", "Pure Composites");

            foreach (var key in nSystem.Keys)
                df.Columns.Add(key.ToString(), typeof(object));

            df.Columns.Add("Prime Factors", typeof(object));

            foreach (Chapter chapter in agent.Client.Selection.Chapters) {

                int prime = Array.BinarySearch<long>(Numbers.Primes, chapter.Value);
                int additPrime = Array.BinarySearch<long>(Numbers.AdditivePrimes, chapter.Value);
                int purePrime = Array.BinarySearch<long>(Numbers.PurePrimes, chapter.Value);
                int composit = Array.BinarySearch<long>(Numbers.Composites, chapter.Value);
                int additComposit = Array.BinarySearch<long>(Numbers.AdditiveComposites, chapter.Value);
                int pureComposit = Array.BinarySearch<long>(Numbers.PureComposites, chapter.Value);

                df.AddEnumeratorRow(new DataFrame.ItemModifierDelegate((v) => { return chapter.Verses.GetLetterFrequency((char)v); }),
                    chapter.GlobalNumber,
                    chapter.Name,
                    chapter.Verses[0].Page.GlobalNumber,
                    chapter.Verses.Count,
                    chapter.Verses.WordCount,
                    chapter.Verses.LetterCount,
                    chapter.Verses.UniqueLetters.Count,
                    chapter.Value,
                    prime == -1 ? (object)"-" : prime,
                    additPrime == -1 ? (object)"-" : additPrime,
                    purePrime == -1 ? (object)"-" : purePrime,
                    composit == -1 ? (object)"-" : composit,
                    additComposit == -1 ? (object)"-" : additComposit,
                    pureComposit == -1 ? (object)"-" : pureComposit,
                    nSystem.GetEnumerator(),
                    chapter.Value.Factorize().ToString(", "));
            }

            agent.SaveCsv(agent.Client.Numerology.ActiveSystem.ToString() + "_ChapterStatistics", df);
        }

        [ResearchMethod(Edition.Research)]
        private static void VerseValueDigitSums(ResearchAgent agent) {

            var df = new DataFrame("Address", "Value", "Digit Sum");

            foreach (Verse verse in agent.Client.Selection.Verses) {
                df.AddRow(verse.Address, verse.Value, verse.Value.DigitSum());
            }

            agent.SaveCsv(agent.Client.Numerology.ActiveSystem.ToString() + "_VerseValueDigitSums", df);
        }

        [ResearchMethod(Edition.Research)]
        private static void ChapterValueDigitSums(ResearchAgent agent) {

            var df = new DataFrame("#", "Value", "Digit Sum");

            foreach (Chapter chapter in agent.Client.Selection.Chapters) {
                df.AddRow(chapter.GlobalNumber, chapter.Value, chapter.Value.DigitSum());
            }

            agent.SaveCsv(agent.Client.Numerology.ActiveSystem.ToString() + "_ChapterValueDigitSums", df);
        }

        [ResearchMethod(Edition.Research)]
        private static void VerseValueWithXDigitSum(ResearchAgent agent, NumberType nType) {

            var df = new DataFrame("#", "Verse", "Address", "Words", "Letters", "Unique", "Value", "DigitSum", "Text");

            int count = 0;

            foreach (Verse verse in agent.Client.Selection.Verses) {
                bool considerVerse = false;
                var dSum = verse.Value.DigitSum();

                switch (nType) {
                    case NumberType.AdditiveComposite:
                        considerVerse = dSum.IsAdditiveComposite();
                        break;
                    case NumberType.AdditivePrime:
                        considerVerse = dSum.IsAdditivePrime();
                        break;
                    case NumberType.Natural:
                        considerVerse = true;
                        break;
                    case NumberType.Composite:
                        dSum.IsComposite();
                        break;
                    case NumberType.Cubic:
                        considerVerse = Numerology.Polygon.IsCubic(Convert.ToUInt32(dSum));
                        break;
                    case NumberType.Even:
                        considerVerse = dSum.IsEven();
                        break;
                    case NumberType.Fibonacci:
                        considerVerse = Numerology.Numbers.Fibonaccis.Contains(dSum);
                        break;
                    case NumberType.MersennePrime:
                        considerVerse = Array.BinarySearch<long>(Numerology.Numbers.MersennePrimes, dSum) != -1;
                        break;
                    case NumberType.Odd:
                        considerVerse = dSum.IsOdd();
                        break;
                    case NumberType.Prime:
                        considerVerse = dSum.IsPrime();
                        break;
                    case NumberType.PureComposite:
                        considerVerse = dSum.IsPureComposite();
                        break;
                    case NumberType.PurePrime:
                        considerVerse = dSum.IsPurePrime();
                        break;
                    case NumberType.Pyramidal:
                        considerVerse = Numerology.Polygon.IsPyramidal((uint)dSum);
                        break;
                    case NumberType.Tetrahedral:
                        considerVerse = Numerology.Polygon.IsTetrahedral((uint)dSum);
                        break;
                }

                if (considerVerse) {
                    count++;
                    df.AddRow(count, verse.GlobalNumber, verse.Address, verse.Words.Count, verse.Words.LetterCount,
                        verse.UniqueLetters.Count, verse.Value, dSum, verse.ActiveText);
                }
            }

            agent.SaveCsv(agent.Client.Numerology.ActiveSystem.ToString() + "_VerseValueWithXDigitSum_" + nType.ToString(), df);
        }

        [ResearchMethod(Edition.Research)]
        private static void ConcatenateChapterVerses(ResearchAgent agent) {
            StringBuilder str = new StringBuilder();
            foreach (Chapter chapter in agent.Client.Selection.Chapters) {
                str.Append(chapter.GlobalNumber);
                foreach (Verse verse in chapter.Verses) {
                    str.Append(verse.NumberInChapter);
                }
            }

            agent.SaveText("ConcatenateChapterVerses", str.ToString());
        }

        [ResearchMethod(Edition.Research)]
        private static void ConcatenateChaptersVerses(ResearchAgent agent) {
            StringBuilder str = new StringBuilder();
            foreach (Chapter chapter in agent.Client.Selection.Chapters) {
                foreach (Verse verse in chapter.Verses) {
                    str.Append(chapter.GlobalNumber);
                    str.Append(verse.NumberInChapter);
                }
            }
            str.Append("\r\n" + str.Length); // 12705 digits != prime 12791 of the Amazing Quran Statistics on heliwave.com

            agent.SaveText("ConcatenateChaptersVerses", str.ToString());
        }

        [ResearchMethod(Edition.Research)]
        private static void WordLetterSums(ResearchAgent agent) {

            var df = new DataFrame("#", "Chapter", "Verse", "Words", "Factors", "Letters", "Factors", "Unique Letters", "Factors", "Sum", "Factors", "PrimeFactors", "Text");

            foreach (Verse verse in agent.Client.Selection.Verses) {
                var sum = verse.Words.Count + verse.Words.LetterCount + verse.UniqueLetters.Count;

                df.AddRow(verse.GlobalNumber, verse.Chapter.GlobalNumber, verse.NumberInChapter,
                    verse.Words.Count, verse.Words.Count.Factorize().ToString(", "),
                    verse.Words.LetterCount, verse.Words.LetterCount.Factorize().ToString(", "),
                    verse.UniqueLetters.Count, verse.UniqueLetters.Count.Factorize().ToString(", "),
                    sum, sum.Factorize().ToString(", "),
                    verse.ActiveText);
            }

            agent.SaveCsv(agent.Client.Numerology.ActiveSystem.ToString() + "_WordLetterSums", df);
        }

    }
}